#![no_std]
#![allow(async_fn_in_trait)]
#![doc = include_str!("../README.md")]

// //! ## Feature flags
// #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]

pub mod clocks; // still provide clock helpers
pub mod gpio;
pub mod pins; // pin mux helpers

pub mod adc;
pub mod clkout;
pub mod config;
pub mod crc;
pub mod i2c;
pub mod interrupt;
pub mod lpuart;
pub mod ostimer;
pub mod rtc;

pub use crate::pac::NVIC_PRIO_BITS;

#[rustfmt::skip]
embassy_hal_internal::peripherals!(
    ADC0,
    ADC1,

    AOI0,
    AOI1,

    CAN0,
    CAN1,

    CDOG0,
    CDOG1,

    // CLKOUT is not specifically a peripheral (it's part of SYSCON),
    // but we still want it to be a singleton.
    CLKOUT,

    CMC,
    CMP0,
    CMP1,
    CRC0,

    CTIMER0,
    CTIMER1,
    CTIMER2,
    CTIMER3,
    CTIMER4,

    DBGMAILBOX,
    DMA0,
    EDMA0_TCD0,
    EIM0,
    EQDC0,
    EQDC1,
    ERM0,
    FLEXIO0,
    FLEXPWM0,
    FLEXPWM1,
    FMC0,
    FMU0,
    FREQME0,
    GLIKEY0,

    GPIO0,
    GPIO1,
    GPIO2,
    GPIO3,
    GPIO4,

    I3C0,
    INPUTMUX0,

    LPI2C0,
    LPI2C1,
    LPI2C2,
    LPI2C3,

    LPSPI0,
    LPSPI1,

    LPTMR0,

    LPUART0,
    LPUART1,
    LPUART2,
    LPUART3,
    LPUART4,
    LPUART5,

    MAU0,
    MBC0,
    MRCC0,
    OPAMP0,

    #[cfg(not(feature = "time"))]
    OSTIMER0,

    P0_0,
    P0_1,
    P0_2,
    P0_3,
    P0_4,
    P0_5,
    P0_6,
    P0_7,
    P0_8,
    P0_9,
    P0_10,
    P0_11,
    P0_12,
    P0_13,
    P0_14,
    P0_15,
    P0_16,
    P0_17,
    P0_18,
    P0_19,
    P0_20,
    P0_21,
    P0_22,
    P0_23,
    P0_24,
    P0_25,
    P0_26,
    P0_27,
    P0_28,
    P0_29,
    P0_30,
    P0_31,

    P1_0,
    P1_1,
    P1_2,
    P1_3,
    P1_4,
    P1_5,
    P1_6,
    P1_7,
    P1_8,
    P1_9,
    P1_10,
    P1_11,
    P1_12,
    P1_13,
    P1_14,
    P1_15,
    P1_16,
    P1_17,
    P1_18,
    P1_19,
    P1_20,
    P1_21,
    P1_22,
    P1_23,
    P1_24,
    P1_25,
    P1_26,
    P1_27,
    P1_28,
    P1_29,
    P1_30,
    P1_31,

    P2_0,
    P2_1,
    P2_2,
    P2_3,
    P2_4,
    P2_5,
    P2_6,
    P2_7,
    P2_8,
    P2_9,
    P2_10,
    P2_11,
    P2_12,
    P2_13,
    P2_14,
    P2_15,
    P2_16,
    P2_17,
    P2_18,
    P2_19,
    P2_20,
    P2_21,
    P2_22,
    P2_23,
    P2_24,
    P2_25,
    P2_26,
    P2_27,
    P2_28,
    P2_29,
    P2_30,
    P2_31,

    P3_0,
    P3_1,
    P3_2,
    P3_3,
    P3_4,
    P3_5,
    P3_6,
    P3_7,
    P3_8,
    P3_9,
    P3_10,
    P3_11,
    P3_12,
    P3_13,
    P3_14,
    P3_15,
    P3_16,
    P3_17,
    P3_18,
    P3_19,
    P3_20,
    P3_21,
    P3_22,
    P3_23,
    P3_24,
    P3_25,
    P3_26,
    P3_27,
    P3_28,
    P3_29,
    P3_30,
    P3_31,

    P4_0,
    P4_1,
    P4_2,
    P4_3,
    P4_4,
    P4_5,
    P4_6,
    P4_7,
    P4_8,
    P4_9,
    P4_10,
    P4_11,
    P4_12,
    P4_13,
    P4_14,
    P4_15,
    P4_16,
    P4_17,
    P4_18,
    P4_19,
    P4_20,
    P4_21,
    P4_22,
    P4_23,
    P4_24,
    P4_25,
    P4_26,
    P4_27,
    P4_28,
    P4_29,
    P4_30,
    P4_31,

    P5_0,
    P5_1,
    P5_2,
    P5_3,
    P5_4,
    P5_5,
    P5_6,
    P5_7,
    P5_8,
    P5_9,
    P5_10,
    P5_11,
    P5_12,
    P5_13,
    P5_14,
    P5_15,
    P5_16,
    P5_17,
    P5_18,
    P5_19,
    P5_20,
    P5_21,
    P5_22,
    P5_23,
    P5_24,
    P5_25,
    P5_26,
    P5_27,
    P5_28,
    P5_29,
    P5_30,
    P5_31,

    PKC0,

    PORT0,
    PORT1,
    PORT2,
    PORT3,
    PORT4,

    RTC0,
    SAU,
    SCG0,
    SCN_SCB,
    SGI0,
    SMARTDMA0,
    SPC0,
    SYSCON,
    TDET0,
    TRNG0,
    UDF0,
    USB0,
    UTICK0,
    VBAT0,
    WAKETIMER0,
    WUU0,
    WWDT0,
);

// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it.

// Re-export interrupt traits and types
pub use adc::Adc1 as Adc1Token;
pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output};
pub use interrupt::InterruptExt;
#[cfg(feature = "unstable-pac")]
pub use mcxa_pac as pac;
#[cfg(not(feature = "unstable-pac"))]
pub(crate) use mcxa_pac as pac;

/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
pub fn init(cfg: crate::config::Config) -> Peripherals {
    let peripherals = Peripherals::take();
    // Apply user-configured priority early; enabling is left to examples/apps
    #[cfg(feature = "time")]
    crate::interrupt::OS_EVENT.set_priority(cfg.time_interrupt_priority);
    // Apply user-configured priority early; enabling is left to examples/apps
    crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);
    // Apply user-configured priority early; enabling is left to examples/apps
    crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority);
    // Apply user-configured priority early; enabling is left to examples/apps
    crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority);
    // Apply user-configured priority early; enabling is left to examples/apps
    crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority);
    // Apply user-configured priority early; enabling is left to examples/apps
    crate::interrupt::GPIO2.set_priority(cfg.gpio_interrupt_priority);
    // Apply user-configured priority early; enabling is left to examples/apps
    crate::interrupt::GPIO3.set_priority(cfg.gpio_interrupt_priority);
    // Apply user-configured priority early; enabling is left to examples/apps
    crate::interrupt::GPIO4.set_priority(cfg.gpio_interrupt_priority);

    // Configure clocks
    crate::clocks::init(cfg.clock_cfg).unwrap();

    unsafe {
        crate::gpio::init();
    }

    // Initialize embassy-time global driver backed by OSTIMER0
    #[cfg(feature = "time")]
    crate::ostimer::time_driver::init(crate::config::Config::default().time_interrupt_priority, 1_000_000);

    // Enable GPIO clocks
    unsafe {
        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT0>(&crate::clocks::periph_helpers::NoConfig);
        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO0>(&crate::clocks::periph_helpers::NoConfig);

        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT1>(&crate::clocks::periph_helpers::NoConfig);
        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO1>(&crate::clocks::periph_helpers::NoConfig);

        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT2>(&crate::clocks::periph_helpers::NoConfig);
        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO2>(&crate::clocks::periph_helpers::NoConfig);

        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT3>(&crate::clocks::periph_helpers::NoConfig);
        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO3>(&crate::clocks::periph_helpers::NoConfig);

        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT4>(&crate::clocks::periph_helpers::NoConfig);
        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO4>(&crate::clocks::periph_helpers::NoConfig);
    }

    peripherals
}

// /// Optional hook called by cortex-m-rt before RAM init.
// /// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state
// /// left by soft resets/debug sessions.
// ///
// /// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor'
// /// feature is incompatible with our setup because it expects __vector_table to be
// /// defined differently than how our RAM-based linker script arranges it.
// #[no_mangle]
// pub unsafe extern "C" fn __pre_init() {
//     // Set the VTOR to point to the interrupt vector table in RAM
//     // This is required since code runs from RAM on this MCU
//     crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32);

//     // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs.
//     let nvic = &*cortex_m::peripheral::NVIC::PTR;
//     for i in 0..4 {
//         // 4 words x 32 = 128 IRQs
//         nvic.icer[i].write(0xFFFF_FFFF);
//         nvic.icpr[i].write(0xFFFF_FFFF);
//     }
//     // Do NOT touch peripheral registers here: clocks may be off and accesses can fault.
//     crate::interrupt::clear_default_handler_snapshot();
// }

/// Internal helper to dispatch a type-level interrupt handler.
#[inline(always)]
#[doc(hidden)]
pub unsafe fn __handle_interrupt<T, H>()
where
    T: crate::interrupt::typelevel::Interrupt,
    H: crate::interrupt::typelevel::Handler<T>,
{
    H::on_interrupt();
}

/// Macro to bind interrupts to handlers, similar to embassy-imxrt.
///
/// Example:
/// - Bind OS_EVENT to the OSTIMER time-driver handler
///   bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; });
#[macro_export]
macro_rules! bind_interrupts {
    ($(#[$attr:meta])* $vis:vis struct $name:ident {
        $(
            $(#[cfg($cond_irq:meta)])?
            $irq:ident => $(
                $(#[cfg($cond_handler:meta)])?
                $handler:ty
            ),*;
        )*
    }) => {
        #[derive(Copy, Clone)]
        $(#[$attr])*
        $vis struct $name;

        $(
            #[allow(non_snake_case)]
            #[no_mangle]
            $(#[cfg($cond_irq)])?
            unsafe extern "C" fn $irq() {
                unsafe {
                    $(
                        $(#[cfg($cond_handler)])?
                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
                    )*
                }
            }

            $(#[cfg($cond_irq)])?
            $crate::bind_interrupts!(@inner
                $(
                    $(#[cfg($cond_handler)])?
                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
                )*
            );
        )*
    };
    (@inner $($t:tt)*) => {
        $($t)*
    }
}
